/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

// draw.c -- this is the only file outside the refresh that touches the vid buffer

#include "quakedef.h"

// used for flushing on load
void Draw_Flush (void);

cvar_t *gl_texturemode;
cvar_t *draw_fadecolor;

qpic_t		*draw_disc;
qpic_t		*draw_backtile;
qpic_t		*conback;

QTEXTURE CrosshairTexture;
QTEXTURE CharacterTexture;
QTEXTURE MapshotTexture;
char MapshotName[MAX_PATH];

typedef struct glpic_s
{
	QTEXTURE *Texture;
	float sl, tl, sh, th;
} glpic_t;


D3D10_FILTER d3d_MinMagMipFilter = D3D10_FILTER_MIN_MAG_MIP_LINEAR;
DWORD d3d_Anisotropy = 1;


//=============================================================================
/* Support Routines */

struct cachepic_t
{
	char		name[MAX_QPATH];
	qpic_t		pic;
	byte		padding[32];	// for appended glpic
};


#define	MAX_CACHED_PICS		128
cachepic_t	menu_cachepics[MAX_CACHED_PICS];
int			menu_numcachepics;

byte	*menuplyr_pixels;

int		pic_texels;
int		pic_count;

/*
================
GL_LoadPicTexture
================
*/
void GL_LoadPicTexture (qpic_t *pic, glpic_t *gl)
{
	pic_texels += pic->width * pic->height;
	pic_count++;

	// load it first otherwise setting the glpic_t params will trash the data
	gl->Texture = D3D_LoadTexture ("", pic->data, pic->width, pic->height, TEX_ALPHA | TEX_NODISCARD);

	// and now it's safe to set these
	gl->sl = 0;
	gl->sh = 1;
	gl->tl = 0;
	gl->th = 1;
}


BOOL Draw_PicFromScrap (glpic_t *gl, int width, int height, byte *data)
{
	// D3D10 FIXME
	int scrap_x, scrap_y;
	QTEXTURE *tex;

	if ((tex = D3D_ScrapUpload (width, height, data, scrap_x, scrap_y)) != NULL)
	{
		gl->Texture = tex;

		gl->sl = (float) scrap_x / (float) SCRAP_SIZE;
		gl->sh = (float) (scrap_x + width) / (float) SCRAP_SIZE;
		gl->tl = (float) scrap_y / (float) SCRAP_SIZE;
		gl->th = (float) (scrap_y + height) / (float) SCRAP_SIZE;

		pic_count++;
		pic_texels += width * height;

		// this texture is now in the scrap
		return TRUE;
	}

	// didn't load or not a scrap pic
	return FALSE;
}


qpic_t *Draw_PicFromWad (char *name)
{
	// D3D10 FIXME
	qpic_t	*p = (qpic_t *) W_GetLumpName (name);
	glpic_t	*gl = (glpic_t *) p->data;

	Draw_Flush ();

	// load little ones into the scrap
	if (Draw_PicFromScrap (gl, p->width, p->height, p->data))
		return p;

	GL_LoadPicTexture (p, gl);

	return p;
}


/*
================
Draw_CachePic
================
*/
qpic_t *Draw_CachePic (char *path)
{
	// d3d10 fixme
	cachepic_t	*pic;
	int			i;
	qpic_t		*dat;

	for (pic = menu_cachepics, i = 0; i < menu_numcachepics; pic++, i++)
		if (!strcmp (path, pic->name))
			return &pic->pic;

	if (menu_numcachepics == MAX_CACHED_PICS)
		Sys_Error ("menu_numcachepics == MAX_CACHED_PICS");

	Draw_Flush ();
	menu_numcachepics++;
	strcpy (pic->name, path);

	// load the pic from disk
	dat = (qpic_t *) FS_LoadFile (path, qScratch);

	if (!dat)
	{
		Sys_Error ("Draw_CachePic: failed to load %s", path);
		return NULL;
	}

	SwapPic (dat);

	// HACK HACK HACK --- we need to keep the bytes for
	// the translatable player picture just for the menu
	// configuration dialog
	if (!strcmp (path, "gfx/menuplyr.lmp"))
	{
		// must be freed before loading otherwise game changes will leak the old pointer
		if (menuplyr_pixels)
		{
			zoneHeap->Free (menuplyr_pixels);
			menuplyr_pixels = NULL;
		}

		if ((menuplyr_pixels = (byte *) zoneHeap->Alloc (dat->width * dat->height)) != NULL)
			memcpy (menuplyr_pixels, dat->data, dat->width * dat->height);
		else Sys_Error ("Draw_CachePic : allocation failed");
	}

	pic->pic.width = dat->width;
	pic->pic.height = dat->height;

	GL_LoadPicTexture (dat, (glpic_t *) pic->pic.data);

	return &pic->pic;
}


typedef struct glmode_s
{
	char *name;
	D3D10_FILTER MinMagMipFilter;
	BOOL Mipmap;
} glmode_t;


glmode_t modes[] =
{
	{"GL_NEAREST", D3D10_FILTER_MIN_MAG_MIP_POINT, FALSE},
	{"GL_LINEAR", D3D10_FILTER_MIN_MAG_MIP_LINEAR, FALSE},
	{"GL_NEAREST_MIPMAP_NEAREST", D3D10_FILTER_MIN_MAG_MIP_POINT, TRUE},
	{"GL_LINEAR_MIPMAP_NEAREST", D3D10_FILTER_MIN_MAG_LINEAR_MIP_POINT, TRUE},
	{"GL_NEAREST_MIPMAP_LINEAR", D3D10_FILTER_MIN_MAG_POINT_MIP_LINEAR, TRUE},
	{"GL_LINEAR_MIPMAP_LINEAR", D3D10_FILTER_MIN_MAG_MIP_LINEAR, TRUE}
};

/*
===============
Draw_TextureMode_f
===============
*/
void Draw_TextureMode_f (void)
{
	// D3D10 FIXME
	/*
	if (!gl_texturemode) return;

	for (int i = 0; i < 6; i++)
	{
		if (!Q_strcasecmp (modes[i].name, gl_texturemode->string))
		{
			// change filters for sampler states
			d3d_MinMagFilter = modes[i].MinMagFilter;
			d3d_MipFilter = modes[i].MipFilter;
			return;
		}
	}
	*/

	Con_Printf (PRINT_DEFAULT, "bad filter name\n");
}


#define MAX_DRAW_QUADS	4096

struct drawquaddef_t
{
	float xl, xh, yl, yh;
	float sl, sh, tl, th;
	DWORD color;
} *d3d_DrawQuads;


int d3d_FirstDrawQuad = 0;
int d3d_NumDrawQuads = 0;

QSHADER d3d_DrawTextured;
QSHADER d3d_DrawColoured;
QSHADER d3d_DrawFadeScreen;
QSHADER d3d_DrawGamma;

ID3D10Buffer *d3d_DrawConstants = NULL;
ID3D10Buffer *d3d_DrawVertexes = NULL;

struct drawconstants_t
{
	QMATRIX orthoMatrix;
	float gamma;
	float padding[3];
};


byte chbase[] = {
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 
	0xff, 0xff, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
};


void Draw_InitPics (void)
{
	// load the stuff specific to a game
	// clear out the current cached mapshot
	Draw_InvalidateMapshot ();

	// clear out the cache pics in case the game has changed
	menu_numcachepics = 0;

	// load the char texture by hand as it needs palette hackery
	byte *draw_chars = (byte *) W_GetLumpName ("conchars");

	// set proper transparent color
	for (int i = 0; i < 128 * 128; i++)
		if (draw_chars[i] == 0)
			draw_chars[i] = 255;

	CharacterTexture.CreateTexture (draw_chars, 128, 128, TEX_ALPHA | TEX_NODISCARD);
	CrosshairTexture.CreateTexture (chbase, 16, 16, TEX_ALPHA | TEX_NODISCARD);

	// get the other pics we need
	conback = Draw_CachePic ("gfx/conback.lmp");
	draw_disc = Draw_PicFromWad ("disc");
	draw_backtile = Draw_PicFromWad ("backtile");
}


void Draw_ShutdownPics (void)
{
	Draw_InvalidateMapshot ();
	CharacterTexture.Release ();
	CrosshairTexture.Release ();
}

/*
===============
Draw_Init
===============
*/
void Draw_Init (void)
{
	gl_texturemode = Cvar_Get ("gl_texturemode", "GL_LINEAR_MIPMAP_LINEAR", CVAR_ARCHIVE, Draw_TextureMode_f);
	draw_fadecolor = Cvar_Get ("draw_fadecolor", "1", CVAR_ARCHIVE);

	// this is a hack because gl_texturemode will be NULL on first invocation
	Draw_TextureMode_f ();
}


class CDrawHandler : public CD3DHandler
{
public:
	void OnCreateDevice (void)
	{
		// create our shaders
		d3d_DrawTextured.CreateShaders (IDR_DRAWSHADER, "DrawTexturedVS", NULL, "DrawTexturedPS", INPUT_LAYOUT_DRAW);
		d3d_DrawColoured.CreateShaders (IDR_DRAWSHADER, "DrawColouredVS", NULL, "DrawColouredPS", INPUT_LAYOUT_DRAW);

		// create our constant buffer
		D3D10_BUFFER_DESC cbDesc = {
			sizeof (drawconstants_t),
			D3D10_USAGE_DEFAULT,
			D3D10_BIND_CONSTANT_BUFFER,
			0,
			0
		};

		d3d_Device->CreateBuffer (&cbDesc, NULL, &d3d_DrawConstants);

		// create our vertex buffer
		D3D10_BUFFER_DESC vbDesc = {
			sizeof (drawquaddef_t) * MAX_DRAW_QUADS,
			D3D10_USAGE_DYNAMIC,
			D3D10_BIND_VERTEX_BUFFER,
			D3D10_CPU_ACCESS_WRITE,
			0
		};

		d3d_Device->CreateBuffer (&vbDesc, NULL, &d3d_DrawVertexes);
		//d3d_DrawFadeScreenPS = D3D_CreatePixelShader (IDR_DRAWSHADER, "DrawFadeScreenPS");
		//d3d_DrawGammaPS = D3D_CreatePixelShader (IDR_DRAWSHADER, "DrawGammaPS");
	}

	void OnReleaseDevice (void)
	{
		// D3D10 FIXME
		Draw_InvalidateMapshot ();
		menu_numcachepics = 0;

		SAFE_RELEASE (d3d_DrawConstants);
		SAFE_RELEASE (d3d_DrawVertexes);

		CrosshairTexture.Release ();
		CharacterTexture.Release ();
	}

	void OnModeChange (void)
	{
		UINT stride = sizeof (drawquaddef_t);
		UINT offset = 0;

		d3d_Device->IASetVertexBuffers (BUFFER_SLOT_DRAW, 1, &d3d_DrawVertexes, &stride, &offset);

		// this cbuffer always goes in slot 0
		d3d_Device->VSSetConstantBuffers (0, 1, &d3d_DrawConstants);
		d3d_Device->GSSetConstantBuffers (0, 1, &d3d_DrawConstants);
		d3d_Device->PSSetConstantBuffers (0, 1, &d3d_DrawConstants);
	}
} d3d_DrawHandler;


void Draw_SetVertex (volatile drawpolyvert_t *vert, float x, float y, DWORD color, float s, float t)
{
	vert->xy[0] = x;
	vert->xy[1] = y;
	vert->color = color;
	vert->st[0] = s;
	vert->st[1] = t;
}


void Draw_BeginBatching (void)
{
	d3d_Device->IASetPrimitiveTopology (D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);

	// clear cached modes/states/etc
	d3d_NumDrawQuads = 0;
	d3d_DrawQuads = NULL;
}


void Draw_Flush (void)
{
	if (d3d_DrawQuads)
	{
		d3d_DrawVertexes->Unmap ();
		d3d_DrawQuads = NULL;
	}

	if (d3d_NumDrawQuads)
	{
		// draw our batched up quads
		d3d_Device->DrawInstanced (4, d3d_NumDrawQuads, 0, d3d_FirstDrawQuad);
		c_draw_call++;

		d3d_FirstDrawQuad += d3d_NumDrawQuads;
		d3d_NumDrawQuads = 0;
	}
}


void Draw_CommonQuad (int x, int y, int w, int h, float sl = 0, float tl = 0, float sh = 1, float th = 1, DWORD color = 0xffffffff)
{
	D3D10_MAP MapType = D3D10_MAP_WRITE_NO_OVERWRITE;

	if (d3d_FirstDrawQuad + d3d_NumDrawQuads + 1 >= MAX_DRAW_QUADS)
	{
		Draw_Flush ();
		MapType = D3D10_MAP_WRITE_DISCARD;
		d3d_FirstDrawQuad = 0;
	}

	if (!d3d_DrawQuads)
	{
		if (FAILED (d3d_DrawVertexes->Map (MapType, 0, (void **) &d3d_DrawQuads)))
			return;

		d3d_DrawQuads += d3d_FirstDrawQuad;
	}

	d3d_DrawQuads->xl = x;
	d3d_DrawQuads->yl = y;
	d3d_DrawQuads->xh = x + w;
	d3d_DrawQuads->yh = y + h;

	d3d_DrawQuads->sl = sl;
	d3d_DrawQuads->sh = sh;
	d3d_DrawQuads->tl = tl;
	d3d_DrawQuads->th = th;

	d3d_DrawQuads->color = color;

	// go to the next quad
	d3d_DrawQuads++;
	d3d_NumDrawQuads++;
}


void Draw_ColouredQuad (int x, int y, int w, int h, DWORD color)
{
	d3d_DrawColoured.Bind ();
	Draw_CommonQuad (x, y, w, h, 0, 0, 0, 0, color);
}


void Draw_TexturedQuad (QTEXTURE *texture, int x, int y, int w, int h, float sl = 0, float tl = 0, float sh = 1, float th = 1, DWORD color = 0xffffffff)
{
	d3d_DrawTextured.Bind ();
	d3d_State->PSSetTexture (0, texture);

	Draw_CommonQuad (x, y, w, h, sl, tl, sh, th, color);
}


void Draw_FullscreenQuad (DWORD color)
{
	Draw_BeginBatching ();
	Draw_CommonQuad (0, 0, vid.Width, vid.Height - vid.SbarLines, 0, 0, 1, 1, color);
	Draw_Flush ();
}


void Draw_CrossHair (void)
{
	extern cvar_t *crosshair;
	extern cvar_t *crosshaircolor;
	extern cvar_t *cl_crossx;
	extern cvar_t *cl_crossy;

	if (crosshair->value)
	{
		Draw_TexturedQuad (
			&CrosshairTexture,
			((vid.Width >> 1) + cl_crossx->value) - 8,
			(((vid.Height - vid.SbarLines) >> 1) + cl_crossy->value) - 8,
			16,
			16,
			0,
			0,
			1,
			1,
			d_8to24table_rgba[crosshaircolor->integer & 255]
		);
	}
}


/*
================
Draw_Character

Draws one 8*8 graphics character with 0 being transparent.
It can be clipped to the top of the screen to allow the console to be
smoothly scrolled off.
================
*/
void Draw_Character (int x, int y, int num)
{
	int row, col;
	float frow, fcol, size;

	num &= 255;

	if (num == 32) return;		// space
	if (y <= -8) return;		// totally off screen

	row = num >> 4;
	col = num & 15;

	frow = row * 0.0625;
	fcol = col * 0.0625;
	size = 0.0625;

	Draw_TexturedQuad (&CharacterTexture, x, y, 8, 8, fcol, frow, fcol + size, frow + size);
}


void Draw_BackwardsCharacter (int x, int y, int num)
{
	int row, col;
	float frow, fcol, size;

	num &= 255;

	if (num == 32) return;		// space
	if (y <= -8) return;		// totally off screen

	row = num >> 4;
	col = num & 15;

	frow = row * 0.0625;
	fcol = col * 0.0625;
	size = 0.0625;

	Draw_TexturedQuad (&CharacterTexture, x, y, 8, 8, fcol + size, frow, fcol, frow + size);
}


/*
================
Draw_String
================
*/
void Draw_String (int x, int y, char *str)
{
	while (*str)
	{
		Draw_Character (x, y, *str);
		str++;
		x += 8;
	}
}

/*
================
Draw_DebugChar

Draws a single character directly to the upper right corner of the screen.
This is for debugging lockups by drawing different chars in different parts
of the code.
================
*/
void Draw_DebugChar (char num)
{
}

/*
=============
Draw_AlphaPic
=============
*/
void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha)
{
	glpic_t *gl = (glpic_t *) pic->data;
	DWORD color = QCOLOR_ARGB ((byte) (alpha * 255.0f), 255, 255, 255);

	Draw_TexturedQuad (gl->Texture, x, y, pic->width, pic->height, gl->sl, gl->tl, gl->sh, gl->th, color);
}


/*
=============
Draw_Pic
=============
*/
void Draw_Pic (int x, int y, qpic_t *pic)
{
	if (pic)
	{
		glpic_t *gl = (glpic_t *) pic->data;

		Draw_TexturedQuad (gl->Texture, x, y, pic->width, pic->height, gl->sl, gl->tl, gl->sh, gl->th);
	}
}


void Draw_Pic (int x, int y, char *name)
{
	if (name)
	{
		qpic_t *pic = Draw_CachePic (name);
		glpic_t *gl = (glpic_t *) pic->data;

		Draw_TexturedQuad (gl->Texture, x, y, pic->width, pic->height, gl->sl, gl->tl, gl->sh, gl->th);
	}
}


void Draw_TranslatePic (qpic_t *pic, byte *translation)
{
	int hunkmark = qScratch->GetLowMark ();
	byte *data = (byte *) qScratch->FastAlloc (pic->width * pic->height);

	Draw_Flush ();

	// translate the data
	for (int i = 0; i < pic->width * pic->height; i++)
		data[i] = translation[menuplyr_pixels[i]];

	// and fill it
	((glpic_t *) pic->data)->Texture->UpdateTexture (data, pic->width, pic->height, TEX_NODISCARD);

	qScratch->FreeToLowMark (hunkmark);
}


/*
================
Draw_ConsoleBackground

================
*/
void Draw_ConsoleBackground (float pct)
{
	glpic_t *gl = (glpic_t *) conback->data;
	DWORD fade = (DWORD) (pct * 320.0f);

	if (pct < 0) pct = 0.0f;
	if (pct > 1.0f) pct = 1.0f;

	if (fade < 0) fade = 0;
	if (fade > 255) fade = 255;

	Draw_TexturedQuad (
		gl->Texture,
		0,
		0,
		vid.Width,
		(float) vid.Height * pct,
		0.0f,
		1.0f - pct,
		1.0f,
		1.0f,
		QCOLOR_ARGB (fade, fade, fade, fade)
	);
}


/*
=============
Draw_TileClear

This repeats a 64 * 64 tile graphic to fill the screen around a sized down
refresh window.
=============
*/
void Draw_TileClear (int x, int y, int w, int h)
{
	Draw_TexturedQuad (((glpic_t *) draw_backtile->data)->Texture, x, y, w, h, x / 64.0f, y / 64.0f, (x + w) / 64.0f, (y + h) / 64.0f);
}


/*
=============
Draw_Fill

Fills a box of pixels with a single color
=============
*/
void Draw_Fill (int x, int y, int w, int h, int c, int alpha)
{
	Draw_ColouredQuad (x, y, w, h, d_8to24table_rgba[c & 255]);
}


void Draw_TextBoxColumn (int x, int y, int lines, char **pics)
{
	Draw_Pic (x, y, pics[0]);

	for (y += 8; lines > 0; lines--, y += 8)
	{
		if (lines > 0)
			Draw_Pic (x, y, pics[2]);
		else Draw_Pic (x, y, pics[1]);
	}

	Draw_Pic (x, y, pics[3]);
}


void Draw_TextBox (int x, int y, int width, int lines)
{
	// pics we're going to draw
	char *tbLeftPics[] = {"gfx/box_tl.lmp", "gfx/box_ml.lmp", "gfx/box_ml.lmp", "gfx/box_bl.lmp"};
	char *tbMidPics[] = {"gfx/box_tm.lmp", "gfx/box_mm.lmp", "gfx/box_mm2.lmp", "gfx/box_bm.lmp"};
	char *tbRightPics[] = {"gfx/box_tr.lmp", "gfx/box_mr.lmp", "gfx/box_mr.lmp", "gfx/box_br.lmp"};

	// draw left side
	Draw_TextBoxColumn (x, y, lines, tbLeftPics);

	// draw middle - pics are 16 wide
	for (x += 8; width > 0; width -= 2, x += 16)
		Draw_TextBoxColumn (x, y, lines, tbMidPics);

	// draw right side
	Draw_TextBoxColumn (x, y, lines, tbRightPics);
}


void Draw_Mapshot (int x, int y, char *name)
{
	// surround it with a text box
	Draw_TextBox (x - 8, y - 8, 16, 16);

	// D3D10 FIXME
	if (stricmp (MapshotName, name))
	{
		// flush pending drawing
		Draw_Flush ();

		// release any current mapshot
		MapshotTexture.Release ();
		strcpy (MapshotName, name);

		// load a new one
		imagedata_t msdesc;

		if (Image_LoadImageFile (name, &msdesc) != NULL)
		{
			MapshotTexture.CreateTexture (msdesc.data, msdesc.width, msdesc.height, TEX_NODISCARD | TEX_32BIT | TEX_BGRA);
		}
	}

	// draw the appropriate texture
	if (MapshotTexture.Texture)
		Draw_TexturedQuad (&MapshotTexture, x, y, 128, 128);
	else Draw_TexturedQuad (((glpic_t *) conback->data)->Texture, x, y, 128, 128);
}


void Draw_InvalidateMapshot (void)
{
	MapshotTexture.Release ();
	MapshotName[0] = 0;
}


//=============================================================================

/*
================
Draw_FadeScreen

================
*/
void Draw_FadeScreen (void)
{
	// capture failed so just draw a normal fade screen
	Draw_ColouredQuad (0, 0, vid.Width, vid.Height, 0xc8000000);

	// D3D10 FIXME
	/*
	QTEXTURE *FadeTexture = NULL;

	d3d_State->SetStateChangeCallback ();
	Draw_Flush ();

	if ((FadeTexture = D3D_CaptureScreen ()) != NULL)
	{
		// no alpha blending please because we're over-writing the screen
		d3d_State->SetRenderState (&rs_drawnoblend);

		// we can't send this through Draw_TexturedQuad because of the weird states so hack them in directly
		// also using stage 1 so that it doesn't interfere with the rest of the draw state
		d3d_State->PSSetSampler (1, D3DTEXF_LINEAR, D3DTEXF_NONE, D3DTADDRESS_CLAMP);
		d3d_State->PSSetTexture (1, FadeTexture);

		// stage 2 holds the palette row texture lookup, using point-sampling for the authentic 1996 look
		d3d_State->PSSetSampler (2, D3DTEXF_POINT, D3DTEXF_NONE, D3DTADDRESS_CLAMP);
		d3d_State->PSSetTexture (2, PaletteRowTextures[draw_fadecolor->integer & 15]);

		// much better than the 3-stage, 20+ line TSS setup (and only 2 lines of shader code needed too)
		d3d_State->VSSetShader (d3d_DrawTexturedVS);
		d3d_State->PSSetShader (d3d_DrawFadeScreenPS);

		// draw as a normal quad
		Draw_CommonQuad (0, 0, vid.Width, vid.Height);

		// and back to normal alpha blending
		d3d_State->SetRenderState (&rs_drawblended);
	}
	else
	{
		// capture failed so just draw a normal fade screen
		Draw_ColouredQuad (0, 0, vid.Width, vid.Height, 0xc8000000);
	}

	Draw_Flush ();
	d3d_State->SetStateChangeCallback (Draw_Flush);
	*/
}


void Draw_PolyBlend (void)
{
	// this stage may be rejected if the polyblend gets merged with an underwater warp
	if (cls.state != ca_connected) return;
	if (!gl_polyblend->value) return;
	if (!v_blend[3]) return;

	Draw_ColouredQuad (0, 0, vid.Width, vid.Height - vid.SbarLines, *((int *) v_blend));

	// disable blending
	v_blend[0] = 0;
}

//=============================================================================

/*
================
Draw_BeginDisc

Draws the little blue disc in the corner of the screen.
Call before beginning any disc IO.
================
*/
void Draw_BeginDisc (void)
{
}


/*
================
Draw_EndDisc

Erases the disc icon.
Call after completing any disc IO
================
*/
void Draw_EndDisc (void)
{
}


void Draw_SetupProjection (void)
{
	// common for 2D GUI stuff and for underwater warp code
	D3D10_VIEWPORT vp = {0, 0, vid.Width, vid.Height, 0, 0};
	drawconstants_t c;

	d3d_Device->RSSetViewports (1, &vp);

	c.orthoMatrix.Identity ();
	c.orthoMatrix.Ortho (0, vid.Width, vid.Height, 0, -1, 1);
	c.gamma = v_gamma->value;

	d3d_Device->UpdateSubresource (d3d_DrawConstants, 0, NULL, &c, 0, 0);
}


/*
================
GL_Set2D

Setup as if the screen was 320*200
================
*/
void GL_Set2D (void)
{
	// setup viewport and projection for 2D GUI
	Draw_SetupProjection ();

	Draw_BeginBatching ();

	d3d_State->OMSetDepthState (d3d_ZmodeNone);
	d3d_State->OMSetBlendState (d3d_BlendAlpha);

	// state changes should trigger a flush of the current draw batch
	d3d_State->SetStateChangeCallback (Draw_Flush);

	// begin with the polyblend
	Draw_PolyBlend ();
}


void GL_End2D (void)
{
	// clear the state changes
	d3d_State->SetStateChangeCallback ();

	// flush any pending drawing
	Draw_Flush ();

	Draw_ApplyGamma ();

	// anything else that's needed at the end of the 2D scene can go here
	V_UpdatePalette ();
}


void Draw_ApplyGamma (void)
{
	/*
	// floating point crap
	if (v_gamma->value < 0.999f || v_gamma->value > 1.001f)
	{
		// as this is the last thing done each frame we just set states but don't need to bother unsetting them
		QTEXTURE *tex = NULL;

		// just grab the screen to a texture
		if ((tex = D3D_CaptureScreen ()) != NULL)
		{
			// unscaled ortho projection
			Draw_SetupProjection ();

			// no alpha blending please because we're over-writing the screen
			d3d_State->SetRenderState (&rs_drawnoblend);

			// we can't send this through Draw_TexturedQuad because of the weird states so hack them in directly
			d3d_State->PSSetSampler (0, D3DTEXF_POINT, D3DTEXF_NONE, D3DTADDRESS_CLAMP);
			d3d_State->PSSetTexture (0, tex);

			// select the proper shaders
			d3d_State->VSSetShader (d3d_DrawTexturedVS);
			d3d_State->PSSetShader (d3d_DrawGammaPS);
			d3d_State->PSSetConstantf (PS_GAMMA, v_gamma->value);

			// draw as a normal quad
			Draw_CommonQuad (0, 0, vid.Width, vid.Height);

			// and run the final flush
			Draw_Flush ();
		}
	}
	*/
}


